<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
    <title>Creating and Rendering Composite Elements - Zend Framework Manual</title>

    <link href="../css/shCore.css" rel="stylesheet" type="text/css" />
    <link href="../css/shThemeDefault.css" rel="stylesheet" type="text/css" />
    <link href="../css/styles.css" media="all" rel="stylesheet" type="text/css" />
</head>
<body>
<h1>Zend Framework</h1>
<h2>Programmer's Reference Guide</h2>
<ul>
    <li><a href="../en/learning.form.decorators.composite.html">Inglês (English)</a></li>
    <li><a href="../pt-br/learning.form.decorators.composite.html">Português Brasileiro (Brazilian Portuguese)</a></li>
</ul>
<table width="100%">
    <tr valign="top">
        <td width="85%">
            <table width="100%">
                <tr>
                    <td width="25%" style="text-align: left;">
                    <a href="learning.form.decorators.individual.html">Rendering Individual Decorators</a>
                    </td>

                    <td width="50%" style="text-align: center;">
                        <div class="up"><span class="up"><a href="learning.form.decorators.html">Entendendo e Usando os Decoradores do Zend Form</a></span><br />
                        <span class="home"><a href="manual.html">Guia de Refer&ecirc;ncia do Programador</a></span></div>
                    </td>

                    <td width="25%" style="text-align: right;">
                        <div class="next" style="text-align: right; float: right;"><a href="learning.form.decorators.conclusion.html">Conclusion</a></div>
                    </td>
                </tr>
            </table>
<hr />
<div id="learning.form.decorators.composite" class="section"><div class="info"><h1 class="title">Creating and Rendering Composite Elements</h1></div>
    

    <p class="para">
        In <a href="learning.form.decorators.individual.html" class="link">the last section</a>, we had an
        example showing a &quot;date of birth element&quot;:
    </p>

    <pre class="programlisting brush: php">
&lt;div class=&quot;element&quot;&gt;
    &lt;?php echo $form-&gt;dateOfBirth-&gt;renderLabel() ?&gt;
    &lt;?php echo $this-&gt;formText(&#039;dateOfBirth[day]&#039;, &#039;&#039;, array(
        &#039;size&#039; =&gt; 2, &#039;maxlength&#039; =&gt; 2)) ?&gt;
    /
    &lt;?php echo $this-&gt;formText(&#039;dateOfBirth[month]&#039;, &#039;&#039;, array(
        &#039;size&#039; =&gt; 2, &#039;maxlength&#039; =&gt; 2)) ?&gt;
    /
    &lt;?php echo $this-&gt;formText(&#039;dateOfBirth[year]&#039;, &#039;&#039;, array(
        &#039;size&#039; =&gt; 4, &#039;maxlength&#039; =&gt; 4)) ?&gt;
&lt;/div&gt;
</pre>


    <p class="para">
        How might you represent this element as a <span class="classname">Zend_Form_Element</span>?
        How might you write a decorator to render it?
    </p>

    <div class="section" id="learning.form.decorators.composite.element"><div class="info"><h1 class="title">The Element</h1></div>
        

        <p class="para">
            The questions about how the element would work include:
        </p>

        <ul class="itemizedlist">
            <li class="listitem">
                <p class="para">
                    How would you set and retrieve the value?
                </p>
            </li>

            <li class="listitem">
                <p class="para">
                    How would you validate the value?
                </p>
            </li>

            <li class="listitem">
                <p class="para">
                    Regardless, how would you then allow for discrete form inputs for the three
                    segments (day, month, year)?
                </p>
            </li>
        </ul>

        <p class="para">
            The first two questions center around the form element itself: how would
             <span class="methodname">setValue()</span> and  <span class="methodname">getValue()</span> work?
            There&#039;s actually another question implied by the question about the decorator: how would
            you retrieve the discrete date segments from the element and/or set them?
        </p>

        <p class="para">
            The solution is to override the  <span class="methodname">setValue()</span> method of your
            element to provide some custom logic. In this particular case, our element should have
            three discrete behaviors:
        </p>

        <ul class="itemizedlist">
            <li class="listitem">
                <p class="para">
                    If an integer timestamp is provided, it should be used to determine and store
                    the day, month, and year.
                </p>
            </li>

            <li class="listitem">
                <p class="para">
                    If a textual string is provided, it should be cast to a timestamp, and then that
                    value used to determine and store the day, month, and year.
                </p>
            </li>

            <li class="listitem">
                <p class="para">
                    If an array containing keys for date, month, and year is provided, those values
                    should be stored.
                </p>
            </li>
        </ul>

        <p class="para">
            Internally, the day, month, and year will be stored discretely. When the value of the
            element is retrieved, it will be done so in a normalized string format. We&#039;ll override
             <span class="methodname">getValue()</span> as well to assemble the discrete date segments into
            a final string.
        </p>

        <p class="para">
            Here&#039;s what the class would look like:
        </p>

        <pre class="programlisting brush: php">
class My_Form_Element_Date extends Zend_Form_Element_Xhtml
{
    protected $_dateFormat = &#039;%year%-%month%-%day%&#039;;
    protected $_day;
    protected $_month;
    protected $_year;

    public function setDay($value)
    {
        $this-&gt;_day = (int) $value;
        return $this;
    }

    public function getDay()
    {
        return $this-&gt;_day;
    }

    public function setMonth($value)
    {
        $this-&gt;_month = (int) $value;
        return $this;
    }

    public function getMonth()
    {
        return $this-&gt;_month;
    }

    public function setYear($value)
    {
        $this-&gt;_year = (int) $value;
        return $this;
    }

    public function getYear()
    {
        return $this-&gt;_year;
    }

    public function setValue($value)
    {
        if (is_int($value)) {
            $this-&gt;setDay(date(&#039;d&#039;, $value))
                 -&gt;setMonth(date(&#039;m&#039;, $value))
                 -&gt;setYear(date(&#039;Y&#039;, $value));
        } elseif (is_string($value)) {
            $date = strtotime($value);
            $this-&gt;setDay(date(&#039;d&#039;, $date))
                 -&gt;setMonth(date(&#039;m&#039;, $date))
                 -&gt;setYear(date(&#039;Y&#039;, $date));
        } elseif (is_array($value)
                  &amp;&amp; (isset($value[&#039;day&#039;])
                      &amp;&amp; isset($value[&#039;month&#039;])
                      &amp;&amp; isset($value[&#039;year&#039;])
                  )
        ) {
            $this-&gt;setDay($value[&#039;day&#039;])
                 -&gt;setMonth($value[&#039;month&#039;])
                 -&gt;setYear($value[&#039;year&#039;]);
        } else {
            throw new Exception(&#039;Invalid date value provided&#039;);
        }

        return $this;
    }

    public function getValue()
    {
        return str_replace(
            array(&#039;%year%&#039;, &#039;%month%&#039;, &#039;%day%&#039;),
            array($this-&gt;getYear(), $this-&gt;getMonth(), $this-&gt;getDay()),
            $this-&gt;_dateFormat
        );
    }
}
</pre>


        <p class="para">
            This class gives some nice flexibility -- we can set default values from our database,
            and be certain that the value will be stored and represented correctly. Additionally,
            we can allow for the value to be set from an array passed via form input. Finally, we
            have discrete accessors for each date segment, which we can now use in a decorator to
            create a composite element.
        </p>
    </div>

    <div class="section" id="learning.form.decorators.composite.decorator"><div class="info"><h1 class="title">The Decorator</h1></div>
        

        <p class="para">
            Revisiting the example from the last section, let&#039;s assume that we want users to input
            each of the year, month, and day separately. <acronym class="acronym">PHP</acronym> fortunately allows
            us to use array notation when creating elements, so it&#039;s still possible to capture these
            three entities into a single value -- and we&#039;ve now created a
            <span class="classname">Zend_Form</span> element that can handle such an array value.
        </p>

        <p class="para">
            The decorator is relatively simple: it will grab the day, month, and year from the
            element, and pass each to a discrete view helper to render individual form inputs; these
            will then be aggregated to form the final markup.
        </p>

        <pre class="programlisting brush: php">
class My_Form_Decorator_Date extends Zend_Form_Decorator_Abstract
{
    public function render($content)
    {
        $element = $this-&gt;getElement();
        if (!$element instanceof My_Form_Element_Date) {
            // only want to render Date elements
            return $content;
        }

        $view = $element-&gt;getView();
        if (!$view instanceof Zend_View_Interface) {
            // using view helpers, so do nothing if no view present
            return $content;
        }

        $day   = $element-&gt;getDay();
        $month = $element-&gt;getMonth();
        $year  = $element-&gt;getYear();
        $name  = $element-&gt;getFullyQualifiedName();

        $params = array(
            &#039;size&#039;      =&gt; 2,
            &#039;maxlength&#039; =&gt; 2,
        );
        $yearParams = array(
            &#039;size&#039;      =&gt; 4,
            &#039;maxlength&#039; =&gt; 4,
        );

        $markup = $view-&gt;formText($name . &#039;[day]&#039;, $day, $params)
                . &#039; / &#039; . $view-&gt;formText($name . &#039;[month]&#039;, $month, $params)
                . &#039; / &#039; . $view-&gt;formText($name . &#039;[year]&#039;, $year, $yearParams);

        switch ($this-&gt;getPlacement()) {
            case self::PREPEND:
                return $markup . $this-&gt;getSeparator() . $content;
            case self::APPEND:
            default:
                return $content . $this-&gt;getSeparator() . $markup;
        }
    }
}
</pre>


    <p class="para">
        We now have to do a minor tweak to our form element, and tell it that we want to use the
        above decorator as a default. That takes two steps. First, we need to inform the element of
        the decorator path. We can do that in the constructor:
    </p>

    <pre class="programlisting brush: php">
class My_Form_Element_Date extends Zend_Form_Element_Xhtml
{
    // ...

    public function __construct($spec, $options = null)
    {
        $this-&gt;addPrefixPath(
            &#039;My_Form_Decorator&#039;,
            &#039;My/Form/Decorator&#039;,
            &#039;decorator&#039;
        );
        parent::__construct($spec, $options);
    }

    // ...
}
</pre>


    <p class="para">
        Note that this is being done in the constructor and not in  <span class="methodname">init()</span>.
        This is for two reasons. First, it allows extending the element later to add logic in
         <span class="methodname">init</span> without needing to worry about calling
         <span class="methodname">parent::init()</span>. Second, it allows passing additional plugin paths
        via configuration or within an  <span class="methodname">init</span> method that will then allow
        overriding the default <span class="classname">Date</span> decorator with my own replacement.
    </p>

    <p class="para">
        Next, we need to override the  <span class="methodname">loadDefaultDecorators()</span> method to use
        our new <span class="classname">Date</span> decorator:
    </p>

    <pre class="programlisting brush: php">
class My_Form_Element_Date extends Zend_Form_Element_Xhtml
{
    // ...

    public function loadDefaultDecorators()
    {
        if ($this-&gt;loadDefaultDecoratorsIsDisabled()) {
            return;
        }

        $decorators = $this-&gt;getDecorators();
        if (empty($decorators)) {
            $this-&gt;addDecorator(&#039;Date&#039;)
                 -&gt;addDecorator(&#039;Errors&#039;)
                 -&gt;addDecorator(&#039;Description&#039;, array(
                     &#039;tag&#039;   =&gt; &#039;p&#039;,
                     &#039;class&#039; =&gt; &#039;description&#039;
                 ))
                 -&gt;addDecorator(&#039;HtmlTag&#039;, array(
                     &#039;tag&#039; =&gt; &#039;dd&#039;,
                     &#039;id&#039;  =&gt; $this-&gt;getName() . &#039;-element&#039;
                 ))
                 -&gt;addDecorator(&#039;Label&#039;, array(&#039;tag&#039; =&gt; &#039;dt&#039;));
        }
    }

    // ...
}
</pre>


    <p class="para">
        What does the final output look like? Let&#039;s consider the following element:
    </p>

    <pre class="programlisting brush: php">
$d = new My_Form_Element_Date(&#039;dateOfBirth&#039;);
$d-&gt;setLabel(&#039;Date of Birth: &#039;)
  -&gt;setView(new Zend_View());

// These are equivalent:
$d-&gt;setValue(&#039;20 April 2009&#039;);
$d-&gt;setValue(array(&#039;year&#039; =&gt; &#039;2009&#039;, &#039;month&#039; =&gt; &#039;04&#039;, &#039;day&#039; =&gt; &#039;20&#039;));
</pre>


    <p class="para">
        If you then echo this element, you get the following markup (with some slight whitespace
        modifications for readability):
    </p>

    <pre class="programlisting brush: html">
&lt;dt id=&quot;dateOfBirth-label&quot;&gt;&lt;label for=&quot;dateOfBirth&quot; class=&quot;optional&quot;&gt;
    Date of Birth:
&lt;/label&gt;&lt;/dt&gt;
&lt;dd id=&quot;dateOfBirth-element&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;dateOfBirth[day]&quot; id=&quot;dateOfBirth-day&quot;
        value=&quot;20&quot; size=&quot;2&quot; maxlength=&quot;2&quot;&gt; /
    &lt;input type=&quot;text&quot; name=&quot;dateOfBirth[month]&quot; id=&quot;dateOfBirth-month&quot;
        value=&quot;4&quot; size=&quot;2&quot; maxlength=&quot;2&quot;&gt; /
    &lt;input type=&quot;text&quot; name=&quot;dateOfBirth[year]&quot; id=&quot;dateOfBirth-year&quot;
        value=&quot;2009&quot; size=&quot;4&quot; maxlength=&quot;4&quot;&gt;
&lt;/dd&gt;
</pre>

    </div>

    <div class="section" id="learning.form.decorators.composite.conclusion"><div class="info"><h1 class="title">Conclusion</h1></div>
        

        <p class="para">
            We now have an element that can render multiple related form input fields, and then
            handle the aggregated fields as a single entity -- the <var class="varname">dateOfBirth</var>
            element will be passed as an array to the element, and the element will then, as we
            noted earlier, create the appropriate date segments and return a value we can use for
            most backends.
        </p>

        <p class="para">
            Additionally, we can use different decorators with the element. If we wanted to use a
            <a href="http://dojotoolkit.org/" class="link external">&raquo; Dojo</a> <span class="classname">DateTextBox</span>
            dijit decorator -- which accepts and returns string values -- we can, with no
            modifications to the element itself.
        </p>

        <p class="para">
            In the end, you get a uniform element <acronym class="acronym">API</acronym> you can use to describe an
            element representing a composite value.
        </p>
    </div>
</div>
        <hr />

            <table width="100%">
                <tr>
                    <td width="25%" style="text-align: left;">
                    <a href="learning.form.decorators.individual.html">Rendering Individual Decorators</a>
                    </td>

                    <td width="50%" style="text-align: center;">
                        <div class="up"><span class="up"><a href="learning.form.decorators.html">Entendendo e Usando os Decoradores do Zend Form</a></span><br />
                        <span class="home"><a href="manual.html">Guia de Refer&ecirc;ncia do Programador</a></span></div>
                    </td>

                    <td width="25%" style="text-align: right;">
                        <div class="next" style="text-align: right; float: right;"><a href="learning.form.decorators.conclusion.html">Conclusion</a></div>
                    </td>
                </tr>
            </table>
</td>
        <td style="font-size: smaller;" width="15%"> <style type="text/css">
#leftbar {
	float: left;
	width: 186px;
	padding: 5px;
	font-size: smaller;
}
ul.toc {
	margin: 0px 5px 5px 5px;
	padding: 0px;
}
ul.toc li {
	font-size: 85%;
	margin: 1px 0 1px 1px;
	padding: 1px 0 1px 11px;
	list-style-type: none;
	background-repeat: no-repeat;
	background-position: center left;
}
ul.toc li.header {
	font-size: 115%;
	padding: 5px 0px 5px 11px;
	border-bottom: 1px solid #cccccc;
	margin-bottom: 5px;
}
ul.toc li.active {
	font-weight: bold;
}
ul.toc li a {
	text-decoration: none;
}
ul.toc li a:hover {
	text-decoration: underline;
}
</style>
 <ul class="toc">
  <li class="header home"><a href="manual.html">Guia de Refer&ecirc;ncia do Programador</a></li>
  <li class="header up"><a href="manual.html">Guia de Refer&ecirc;ncia do Programador</a></li>
  <li class="header up"><a href="learning.html">Conhecendo o Zend Framework</a></li>
  <li class="header up"><a href="learning.form.decorators.html">Entendendo e Usando os Decoradores do Zend Form</a></li>
  <li><a href="learning.form.decorators.intro.html">Introduction</a></li>
  <li><a href="learning.form.decorators.simplest.html">Decorator Basics</a></li>
  <li><a href="learning.form.decorators.layering.html">Layering Decorators</a></li>
  <li><a href="learning.form.decorators.individual.html">Rendering Individual Decorators</a></li>
  <li class="active"><a href="learning.form.decorators.composite.html">Creating and Rendering Composite Elements</a></li>
  <li><a href="learning.form.decorators.conclusion.html">Conclusion</a></li>
 </ul>
 </td>
    </tr>
</table>

<script type="text/javascript" src="../js/shCore.js"></script>
<script type="text/javascript" src="../js/shAutoloader.js"></script>
<script type="text/javascript" src="../js/main.js"></script>

</body>
</html>